/*
 * Speck cipher implementation and testing code
 * (c) 2013 by Sebastian Gesemann
 *
 * Speck is a family of lightweight block ciphers designed by a team
 * of NSA members (see http://eprint.iacr.org/2013/404.pdf )
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef SG_SPECK_HPP_INCLUDED
#define SG_SPECK_HPP_INCLUDED

#include <cassert>
#include <algorithm>
#include <vector>
#include "integer.hpp"

/// This namespace contains a generic C++ implementation of the Speck
/// family of block ciphers.
namespace speck {

// N is the word size in bits
// M is the number of words in a key
// R is the number of rounds
// alpha and beta are word-size dependent rotation constants
// round_base = R-M (is also a word-size dependent constant)

template<int A, int B, int RmM> struct params
{
	static const unsigned alpha      = A;
	static const unsigned beta       = B;
	static const unsigned round_base = RmM;
};

template<int N> struct params_for_N;
template<> struct params_for_N<16> : params<7,2,18> {};
template<> struct params_for_N<24> : params<8,3,19> {};
template<> struct params_for_N<32> : params<8,3,23> {};
template<> struct params_for_N<48> : params<8,3,26> {};
template<> struct params_for_N<64> : params<8,3,30> {};

template<int N>
inline void fwd_round(
	typename integer::select_unsigned<N>::type& x,
	typename integer::select_unsigned<N>::type& y,
	typename integer::select_unsigned<N>::type round_key)
{
	typedef integer::ops<N> o;
	typedef params_for_N<N> p;
	x = o::add(o::rcs(x, p::alpha), y) ^ round_key;
	y = o::lcs(y, p::beta) ^ x;
}

template<int N>
inline void inv_round(
	typename integer::select_unsigned<N>::type& x,
	typename integer::select_unsigned<N>::type& y,
	typename integer::select_unsigned<N>::type round_key)
{
	typedef integer::ops<N> o;
	typedef params_for_N<N> p;
	y = o::rcs(y ^ x, p::beta);
	x = o::lcs(o::sub(x ^ round_key, y), p::alpha);
}

template<int N>
struct block_t
{
	typedef typename integer::select_unsigned<N>::type value_type;
	value_type x; // high word   ( in terms of the authors'...
	value_type y; // low  word    ...test vector representation)
};

/// It turns M key words (2,3 or 4) into a series of R subkeys.
/// The iterators may alias the same sequence.
template<int N, class KeywordInputIter, class SubkeyOutputIter>
inline void key_expand(int M, int R, KeywordInputIter kit, SubkeyOutputIter sit)
{
	assert(2<=M && M<=4);
	typename block_t<N>::value_type tmp[4];
	for (int k=0; k<M; ++k) {
		tmp[k] = *kit;
		++kit;
	}
	*sit = tmp[0];
	++sit;
	for (int i=0; i<(R-1); ++i) {
		fwd_round<N>(tmp[1+(i%(M-1))],tmp[0],i);
		*sit = tmp[0];
		++sit;
	}
}

/// encrypts a block
/// @param io - block
/// @param skBegin - begin iterator of subkey sequence
/// @param skEnd   - end   iterator of subkey sequence
template<int N, class InputIter>
void encrypt(block_t<N> & io, InputIter skBegin, InputIter skEnd)
{
	while (skBegin!=skEnd) {
		fwd_round<N>(io.x,io.y,*skBegin);
		++skBegin;
	}
}

/// decrypts a block
/// @param io - block
/// @param skBegin - begin iterator of subkey sequence
/// @param skEnd   - end   iterator of subkey sequence
template<int N, class BidiIter>
void decrypt(block_t<N> & io, BidiIter skBegin, BidiIter skEnd)
{
	while (skBegin!=skEnd) {
		--skEnd;
		inv_round<N>(io.x,io.y,*skEnd);
	}
}


//----------[ convenience wrappers... ]----------

template<int N, int M>
struct key_t
{
	typedef typename block_t<N>::value_type value_type;
	typedef unsigned int size_type;
	typedef value_type      *       iterator;
	typedef value_type const* const_iterator;
	value_type words[M];
	value_type const& operator[](unsigned i) const {assert(i<M); return words[i];}
	value_type      & operator[](unsigned i)       {assert(i<M); return words[i];}
	iterator begin() {return words+0;}
	iterator end()   {return words+M;}
	const_iterator begin() const {return words+0;}
	const_iterator end()   const {return words+M;}
	size_type size() const {return M;}
};

template<int N, int R>
struct subkeys_t
{
	typedef typename integer::select_unsigned<N>::type value_type;
	typedef unsigned int size_type;
	typedef value_type      *       iterator;
	typedef value_type const* const_iterator;
	value_type words[R];
	value_type const& operator[](unsigned i) const {assert(i<R); return words[i];}
	value_type      & operator[](unsigned i)       {assert(i<R); return words[i];}
	iterator begin() {return words+0;}
	iterator end()   {return words+R;}
	const_iterator begin() const {return words+0;}
	const_iterator end()   const {return words+R;}
	size_type size() const {return R;}
};

template<int N, int M>
inline subkeys_t<N,params_for_N<N>::round_base+M>
key_expand(key_t<N,M> const& key)
{
	const int R = params_for_N<N>::round_base+M;
	subkeys_t<N,R> subkeys;
	key_expand<N>(M,R,key.begin(),subkeys.begin());
	return subkeys;
}

template<int N, class KeywordContainer>
inline std::vector<typename block_t<N>::value_type>
key_expand_to_vector(KeywordContainer const& k, int R = 0)
{
	const int M = k.size();
	const int defaultR = params_for_N<N>::rounds_base + M;
	if (!R) R = defaultR;
	std::vector<typename block_t<N>::value_type> subkeys(R);
	key_expand<N>(M,R,k.begin(),subkeys.begin());
	return subkeys;
}

template<int N, class SubkeyContainer>
inline void encrypt(block_t<N> & io, SubkeyContainer const& subkeyc)
{
	encrypt<N>(io,subkeyc.begin(),subkeyc.end());
}

template<int N, class SubkeyContainer>
inline void decrypt(block_t<N> & io, SubkeyContainer const& subkeyc)
{
	decrypt<N>(io,subkeyc.begin(),subkeyc.end());
}

} // namespace speck

#endif
